package com.zkadmin.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.zkoss.zul.DefaultTreeModel;
import org.zkoss.zul.DefaultTreeNode;

import com.mysema.query.types.Predicate;
import com.mysema.query.types.expr.BooleanExpression;
import com.zkadmin.entity.QSysMenu;
import com.zkadmin.entity.SysMenu;
import com.zkadmin.entity.SysRole;
import com.zkadmin.repository.SysMenuRepository;
import com.zkadmin.repository.SysRoleRepository;
import com.zkadmin.service.SysMenuService;
import com.zkadmin.util.AdminConstants;

@Service("sysMenuService")
public class SysMenuServiceImpl implements SysMenuService {
	@Autowired
	private SysMenuRepository repository;

	@Autowired
	private SysRoleRepository roleRepository;

	public SysMenu save(SysMenu menu) {
		return repository.save(menu);
	}

	@Override
	public List<SysMenu> getRootMenus() {
		return getMenus(null);
	}

	private List<SysMenu> getMenus(final String code) {
		StringBuilder codes = new StringBuilder();
		String likeCode = "";
		int codeLen = 2;
		if (code != null) {
			codeLen = likeCode.length() + 2;
			likeCode = code;
		}

		if (codeLen % 2 == 0) {
			for (int i = 0; i < codeLen / 2; i++) {
				codes.append("__");
			}
		}
		return repository.findByCodeLike(likeCode + codes, new Sort(new Order(Direction.DESC, "sort")));
	}

	private List<DefaultTreeNode<SysMenu>> getMenuTreeNodes(List<SysMenu> menus, boolean isMenuTree) {
		List<DefaultTreeNode<SysMenu>> results = new ArrayList<DefaultTreeNode<SysMenu>>();
		for (SysMenu menu : menus) {
			if (isMenuTree) {
				if (!SecurityUtils.getSubject().hasRole(AdminConstants.ROLE_ADMIN) && !SecurityUtils.getSubject().isPermitted(AdminConstants.MENU_RESOURCE + menu.getCode())) {
					continue;
				}
			}
			List<DefaultTreeNode<SysMenu>> subMenus = getMenuTreeNodes(getMenus(menu.getCode()), isMenuTree);
			results.add(subMenus.isEmpty() ? new DefaultTreeNode<SysMenu>(menu) : new DefaultTreeNode<SysMenu>(menu, subMenus));
		}
		return results;
	}

	@Override
	public DefaultTreeModel<SysMenu> getMenuTreeModel(boolean isMenuTree) {
		return getMenuTreeModel(false, isMenuTree);
	}

	@Override
	public DefaultTreeModel<SysMenu> getMenuTreeModel(boolean isMultiple, boolean isMenuTree) {
		DefaultTreeModel<SysMenu> treeModel = new DefaultTreeModel<SysMenu>(new DefaultTreeNode<SysMenu>(null, getMenuTreeNodes(getRootMenus(), isMenuTree)));
		treeModel.setMultiple(isMultiple);
		return treeModel;
	}

	@Override
	public Page<SysMenu> paging(Predicate predicate, Pageable pageable) {
		return repository.findAll(predicate, pageable);
	}

	@Override
	public Page<SysMenu> findGrouped(Pageable pageable) {
		return repository.findGrouped(pageable);
	}

	@Override
	public void updateCode(SysMenu menu, String newCode) {
		int len = newCode.length();
		if (len % 2 == 0) {
			List<SysMenu> subMenus = repository.findByCodeLike(menu.getCode() + "%");
			for (SysMenu subMenu : subMenus) {
				subMenu.setCode(newCode + ((subMenu.getId() == menu.getId()) ? "" : subMenu.getCode().substring(len)));
				repository.save(subMenu);
			}
		}
	}

	@Override
	public void delete(Long id) {
		SysMenu delMenu = repository.findOne(id);
		if (delMenu != null) {
			List<SysMenu> menus = repository.findByCodeLike(delMenu.getCode() + "%");
			for (SysMenu menu : menus) {
				Set<SysRole> roles = menu.getRoles();
				for (SysRole role : roles) {
					role.removeMenu(menu);
				}
				repository.delete(menu);
			}
		}
	}

	@Override
	public long getMaxMenuCode(SysMenu menu) {
		QSysMenu query = QSysMenu.sysMenu;
		BooleanExpression codeLike = query.code.like(menu.getCode() + "__");
		BooleanExpression idNe = query.id.ne(menu.getId());
		Iterable<SysMenu> sysMenus = repository.findAll(idNe.and(codeLike));
		List<Long> codes = new ArrayList<Long>();
		for (SysMenu sysMenu : sysMenus) {
			codes.add(Long.parseLong(sysMenu.getCode()));
		}

		return codes.isEmpty() ? Long.parseLong(menu.getCode() + "00") : Collections.max(codes);
	}

	@Override
	public SysMenu findByName(String name) {
		return repository.findByName(name);
	}

	@Override
	public SysMenu findByCode(String code) {
		return repository.findByCode(code);
	}

	@Override
	public List<SysMenu> findByCodeLike(String code) {
		return repository.findByCodeLike(code);
	}

}
